# !/usr/bin/env python
# -*-coding:utf-8 -*-

"""
# File       : process_version_view.py
# Time       ：2023/11/3 16:51
# Author     ：YangYong
# version    ：python 3.10
# Description：流程版本
"""
import uuid
from copy import deepcopy
from typing import List, Union

from flask import request

from apis.flow import flow_ns
from apis.flow.schema.task_node_schema import ProcessVersionSchema
from apis.orderlines.models import Process, Task, Variable
from apis.orderlines.schema.process_schema import ProcessSchema
from apis.orderlines.schema.task_schema import TaskSchema
from apis.orderlines.schema.variable_schema import VariableSchema
from public.api_utils.api_decorator import handle_api_error
from public.base_model import db
from public.base_response import generate_response
from public.base_view import AbstractView
from public.logger import logger
from public.utils.mongo_utils import MongoDBUtil


class ProcessVersionView(AbstractView):
    url = '/process_version'

    def __init__(self, *args, **kwargs):
        super(ProcessVersionView, self).__init__(*args, **kwargs)
        if request.method == 'GET':
            self.form_data = request.args
        else:
            self.form_data = request.json
        self.process_id = self.form_data.get('process_id')
        self.process_name = self.form_data.get('process_name')
        self.version = self.form_data.get('version')
        self.version_desc = self.form_data.get('version_desc')
        self.mongo = MongoDBUtil('flow_data')
        self.new_process_id = uuid.uuid4().hex
        self.node_relation = list()
        self.is_logger = True

    @property
    def task_mongo(self):
        return MongoDBUtil('flow_task_data')

    @staticmethod
    def create_process(process_info):
        """创建流程"""
        insert_data = dict()
        for key, val in process_info.items():
            if hasattr(Process, key) and key not in ['process_instance', 'task', 'task_instance']:
                insert_data[key] = val
        with db.auto_commit():
            obj = Process(**insert_data)
            db.session.add(obj)

    def get_version_task_id(self, task_id: str):
        """根据任务名称获取新版本下的task id"""
        obj = db.session.query(Task).filter(Task.task_id == task_id).first()
        task_info = TaskSchema().dump(obj)

        version_task_obj = db.session.query(Task).filter(
            Task.process_id == self.new_process_id,
            Task.task_name == task_info.get('task_name'),
            Task.task_index_id == task_info.get('task_index_id'),
            Task.active == 1
        ).first()
        version_task_info = TaskSchema().dump(version_task_obj)
        return version_task_info.get('task_id')

    def version_process_control_param(self, status_param: dict):
        """根据任务状态寻找流程控制参数"""
        if status_param.get('pc_type') == 'status':
            version_status_param = deepcopy(status_param)
            conditions = version_status_param.get('conditions')
            for item in conditions:
                task_id = item.get('task_id')
                version_task_id = self.get_version_task_id(task_id)
                item['task_id'] = version_task_id
                condition_temps = item.get('condition')
                for temp in condition_temps:
                    condition_task_id = temp.get('condition')
                    temp['condition'] = self.get_version_task_id(condition_task_id)
            return version_status_param
        elif status_param.get('pc_type') == 'result':
            version_result_param = deepcopy(status_param)
            conditions = version_result_param.get('conditions')
            for item in conditions:
                task_id = item.get('task_id')
                item['task_id'] = self.get_version_task_id(task_id)
            return version_result_param
        else:
            raise ValueError('创建版本失败，流程控制参数找不到')

    def get_group_task_param(self, group_task_id: str):
        """获取group任务的参数"""
        obj = db.session.query(Task).filter(Task.task_id == group_task_id, Task.active == 1).first()
        task_info = TaskSchema().dump(obj)
        method_kwargs = task_info.get('method_kwargs')
        new_group_ids = list()
        for task_id in method_kwargs.get('group_ids'):
            version_task_id = self.get_version_task_id(task_id)
            new_group_ids.append(version_task_id)
        method_kwargs['group_ids'] = new_group_ids
        return method_kwargs

    def create_task(self, task_node: List[dict]):
        """创建任务"""
        process_control_tasks = list()
        group_tasks = list()
        for node in task_node:
            task = dict()
            for key, val in node.items():
                if hasattr(Task, key) and key not in ['task_instance', 'id', 'task_id']:
                    task[key] = val
            version_task_id = uuid.uuid4().hex
            old_task_id = node.get('task_id')
            self.node_relation.append({'old_task_id': old_task_id, 'version_task_id': version_task_id})
            task['task_id'] = version_task_id
            task['process_id'] = self.new_process_id
            # 获取mongodb中的数据并重新设置
            flow_task_node = self.get_task_node(old_task_id)
            flow_task_node['task_id'] = version_task_id
            flow_task_node['process_id'] = self.new_process_id
            self.task_mongo.set_value(**flow_task_node)
            # 设置mysql参数数据
            with db.auto_commit():
                if task.get('method_name') == 'process_control':
                    process_control_tasks.append(task.get('task_id'))
                elif task.get('method_name') == 'task_group':
                    group_tasks.append(task.get('task_id'))
                obj = Task(**task)
                db.session.add(obj)

        # 重新设置流程控制的参数
        if process_control_tasks:
            for pc_task_id in process_control_tasks:
                obj = db.session.query(Task).filter(Task.task_id == pc_task_id, Task.active == 1).first()
                task = TaskSchema().dump(obj)
                params = self.version_process_control_param(task['method_kwargs'])
                with db.auto_commit():
                    db.session.query(Task).filter(Task.task_id == pc_task_id, Task.active == 1).update(
                        {'method_kwargs': params})

        # 重新设置任务组的参数
        if group_tasks:
            for task_id in group_tasks:
                params = self.get_group_task_param(task_id)
                with db.auto_commit():
                    db.session.query(Task).filter(
                        Task.task_id == task_id, Task.active == 1
                    ).update({'method_kwargs': params})

    def get_flow_data(self):
        filter_ = {'process_id': self.form_data.get('process_id')}
        flow_data = self.mongo.collection.find_one(filter_)
        if '_id' in flow_data:
            flow_data.pop('_id')
        return flow_data

    def get_relation_task_id(self, task_id) -> Union[str, None]:
        """获取旧任务id对应的新的version_task_id"""
        for item in self.node_relation:
            if item.get('old_task_id') == task_id:
                return item.get('version_task_id')
        return None

    def create_flow_data(self):
        """创建流程图"""
        flow_data = self.get_flow_data()
        edges = flow_data.get('graphData').get('edges')
        nodes = flow_data.get('graphData').get('nodes')
        # 处理节点数据
        version_nodes = list()
        for node in nodes:
            task_id = node.get('id')
            version_task_id = self.get_relation_task_id(task_id)
            node['id'] = version_task_id

            if node.get('type') == 'group-node':
                # 获取groups_ids
                params = self.get_group_task_param(task_id)
                node['children'] = params.get('group_ids')
            version_nodes.append(node)
        # 处理节点线条
        version_edges = list()
        for edge in edges:
            source_node_id = edge.get('sourceNodeId')
            new_source_node_id = self.get_relation_task_id(source_node_id)
            edge['sourceNodeId'] = new_source_node_id

            target_node_id = edge.get('targetNodeId')
            new_target_node_id = self.get_relation_task_id(target_node_id)
            edge['targetNodeId'] = new_target_node_id
            version_edges.append(edge)
        new_graph_data = {
            'nodes': version_nodes,
            'edges': version_edges
        }
        new_flow_data = {
            'process_id': self.new_process_id,
            'graphData': new_graph_data
        }
        filter_ = {'process_id': self.new_process_id}
        obj = self.mongo.collection.find_one(filter_)
        if obj:
            self.mongo.collection.update_one(filter=filter_, update={'$set': new_flow_data}, upsert=True)
            logger.info('mongo db数据保存成功')
        else:
            self.mongo.collection.insert_one(new_flow_data)
            logger.info('mongo db数据插入成功')

    def get_task_node(self, task_id):
        """
        获取mongo中flow的任务节点数据
        @param task_id: 旧版本任务id
        @return:
        """
        flow_task_data = self.task_mongo.get_value(self.process_id, task_id)
        if flow_task_data and '_id' in flow_task_data:
            flow_task_data.pop('_id')
        return flow_task_data

    def check_process_version(self, process_name):
        """
        检查流程版本
        @param process_name:
        @return:
        """
        obj = db.session.query(Process).filter(
            Process.process_name == process_name,
            Process.version == self.version,
            Process.active == 1
        ).first()
        if obj:
            raise ValueError('相同流程，版本名称不能重复')

    def get_task_nodes(self):
        objs = db.session.query(Task).filter(Task.process_id == self.process_id, Task.active == 1).all()
        return TaskSchema().dump(objs, many=True)

    def create_process_variable(self):
        """创建流程变量"""
        objs = db.session.query(Variable).filter(Variable.process_id == self.process_id, Variable.active == 1).all()
        variable_infos = VariableSchema().dump(objs, many=True)
        for variable in variable_infos:
            variable['process_id'] = self.new_process_id
            variable.pop('id')
            with db.auto_commit():
                variable_obj = Variable(**variable)
                db.session.add(variable_obj)

    @handle_api_error()
    @flow_ns.doc(description='创建流程副本')
    def post(self):
        """创建流程副本"""
        if not self.process_id or not self.version:
            raise ValueError('流程id或版本名称不能缺少')
        obj = db.session.query(Process).filter(Process.process_id == self.process_id, Process.active == 1).first()
        process_info = ProcessSchema().dump(obj)

        self.check_process_version(process_info.get('process_name'))
        process_info['version'] = self.version
        process_info['desc'] = process_info.get('desc') or "" + "版本：" + self.version
        process_info['version_desc'] = self.version_desc or f'版本：{self.version}'
        process_info['process_id'] = self.new_process_id
        if process_info.get('id'):
            process_info.pop('id')
        # 创建流程
        self.create_process(process_info)
        task_nodes = self.get_task_nodes()
        # 创建变量
        self.create_process_variable()
        # 创建任务
        self.create_task(task_nodes)
        # 创建流程图数据
        self.create_flow_data()
        return generate_response(data={'process_id': self.new_process_id})

    @handle_api_error()
    @flow_ns.doc(description='获取流程副本')
    def get(self):
        """获取流程副本"""
        if self.process_name:
            obj = db.session.query(Process).filter(
                Process.process_name == self.process_name, Process.active == 1).all()
        else:
            obj = db.session.query(Process).filter(
                Process.process_id == self.process_id, Process.active == 1).all()
        info = ProcessVersionSchema().dump(obj, many=True)
        return generate_response(data=info, message='流程版本获取成功')
